/**
 * 使用信号灯集进行进程间的同步控制
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

typedef union semun {
    int val;
} semun_t;

typedef struct share_memory {
    pid_t pid;
    char data[32];
} shm_t;

int main(int argc, const char *argv[]) {
    key_t key;
    int shmid, semid, semnum, flag;
    shm_t *myshm;
    semnum = 2;
    flag = 0;

    key = ftok("/", 1);

    shmid = shmget(key, sizeof(shm_t), IPC_CREAT | IPC_EXCL | 0664);
    if(shmid == -1) {
        if(errno == EEXIST) {
            shmid = shmget(key, sizeof(shm_t), 0664);
            printf("共享内存已经创建\n");
            myshm = shmat(shmid, NULL, 0);
        } else {
            perror("共享内存创建失败\n");
            _exit(1);
        }
    } else {
        myshm = shmat(shmid, NULL, 0);
        printf("共享内存创建成功\n");
    }

    semid = semget(key, semnum, IPC_CREAT | IPC_EXCL | 0664);
    if(semid == -1) {
        if(errno == EEXIST) {
            printf("信号灯集已经创建\n");
            semid = semget(key, semnum, 0664);
            flag = 1;
        } else {
            perror("信号灯集创建失败");
            _exit(1);
        }
    } else {
        semun_t mysemun;
        mysemun.val = 1;
        if(semctl(semid, 0, SETVAL, mysemun) != 0) {
            printf("初始化信号灯集初始值失败\n");
            _exit(1);
        }
        mysemun.val = 0;
        if(semctl(semid, 1, SETVAL, mysemun) != 0) {
            printf("初始化信号灯集初始值失败\n");
            _exit(1);
        }
        printf("信号灯集创建成功\n");
    }

    struct sembuf mysem;

    while(1) {
        if(flag == 0) {
            mysem.sem_num = 0;
            mysem.sem_op = -1;
            mysem.sem_flg = 0;
            if(semop(semid, &mysem, 1) == -1) {
                perror("申请信号灯[0]失败\n");
                _exit(1);
            }

            scanf("%s", myshm->data);

            mysem.sem_num = 1;
            mysem.sem_op = 1;
            mysem.sem_flg = 0;
            if(semop(semid, &mysem, 1) == -1) {
                perror("释放信号灯[1]失败\n");
                _exit(1);
            }
            if(strncmp(myshm->data, "quit", 4) == 0) {
                break;
            }
        } else {
            mysem.sem_num = 1;
            mysem.sem_op = -1;
            mysem.sem_flg = 0;
            if(semop(semid, &mysem, 1) == -1) {
                perror("申请信号灯[1]失败\n");
                _exit(1);
            }
        
            printf("接收到的数据为：%s\n", myshm->data);

            mysem.sem_num = 0;
            mysem.sem_op = 1;
            mysem.sem_flg = 0;
            if(semop(semid, &mysem, 1) == -1) {
                perror("释放信号灯[0]失败\n");
                _exit(1);
            }
            if(strncmp(myshm->data, "quit", 4) == 0) {
                break;
            }
        }
    
    }

    if(flag == 0) {
        ;
    } else {
        shmdt(myshm);
        shmctl(shmid, IPC_RMID, 0);
        semctl(semid, 0, IPC_RMID);
    }

    return 0;
}

